package l2ft.gameserver.hwid;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import l2ft.commons.threading.RunnableImpl;
import l2ft.gameserver.Config;
import l2ft.gameserver.ThreadPoolManager;
import l2ft.gameserver.database.DatabaseFactory;
import l2ft.gameserver.database.MySql;
import l2ft.gameserver.hwid.HwidGamer.PLAYER_THREAT;
import l2ft.gameserver.model.Player;
import l2ft.gameserver.utils.Log;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HwidEngine
{
	private static final Logger _log = LoggerFactory.getLogger(HwidEngine.class);
	private static HwidEngine _instance;
	
	private List<HwidGamer> _allHwids = new CopyOnWriteArrayList<>();
	
	private HwidEngine()
	{
		ThreadPoolManager.getInstance().scheduleAtFixedRate(new EveryMinuteThread(), 60000L, 60000L);
	}
	
	public HwidGamer getGamerByHwid(String hwid)
	{
		for(HwidGamer gamer : _allHwids)
			if(gamer.getHwid().equals(hwid))
				return gamer;
		return null;
	}
	
	public HwidGamer newPlayer(Player player)
	{
		String hwid = player.getHWID();
		for(HwidGamer gamer : _allHwids)
			if(gamer.getHwid().equals(hwid))
			{
				System.out.println("found same hwid");
				gamer.addPlayer(player);
				return gamer;
			}
		System.out.println("loading from database1");
		//First logged char on this pc, loading from db
		HwidGamer newHwid = loadHwidFromDatabase(hwid);
		newHwid.addPlayer(player);
		_allHwids.add(newHwid);
		return newHwid;
	}
	
	/**
	 * @return null if can, otherwise it @returns reason
	 */
	public String canILog(String hwid)
	{
		for(HwidGamer gamer : _allHwids)
			if(gamer.getHwid().equals(hwid))
			{
				System.out.println("can i log?:"+(gamer.getOnlineChars().size() < Config.MAX_CHARS_PER_PC)+" online chars:"+gamer.getOnlineChars().size()+" max cahrs:"+Config.MAX_CHARS_PER_PC);
				if(gamer.getOnlineChars().size() < Config.MAX_CHARS_PER_PC)
					return "Only "+Config.MAX_CHARS_PER_PC+" character"+(Config.MAX_CHARS_PER_PC > 1 ? "s" : "")+" may be online on the same PC!";
			}
		System.out.println("i can log, first hwid");
		if(isHwidBannedFromDB(hwid))
			return "You are banned!";
		return null;
	}
	
	public void logFailedLogin(Player player)
	{
		String hwid = player.getHWID();
		for(HwidGamer gamer : _allHwids)
			if(gamer.getHwid().equals(hwid))
			{
				Log.LogHwid(gamer, player.getName(), "Login", "Failed! Too many online characters!");
				return;
			}
	}
	
	public void removeGamer(HwidGamer gamer)
	{
		_allHwids.remove(gamer);
	}

	public void banHwid(String hwidToBan)
	{
		HwidGamer gamer = getGamerByHwid(hwidToBan);
		if(gamer != null)
		{
			gamer.setHwidBanned(true);
			for(Player player : gamer.getOnlineChars())
			{
				player.kick();
			}
		}
		banHwidInDb(hwidToBan);
	}
	
	private boolean isHwidBannedFromDB(String hwid)
	{
		try (Connection con = DatabaseFactory.getConnection();
				PreparedStatement statement = con.prepareStatement("SELECT banned FROM hwid WHERE HWID=?"))
		{
			statement.setString(1, hwid);
			
			ResultSet rset = statement.executeQuery();
			if(rset.next())
			{
				return rset.getBoolean("banned");
			}
		}
		catch(Exception e)
		{
			_log.error("Failed to load Hwid("+hwid+") from Database: ", e);
		}
		return false;
	}
	
	private HwidGamer loadHwidFromDatabase(String hwid)
	{
		HwidGamer foundGamer = null;
		
		try (Connection con = DatabaseFactory.getConnection();
				PreparedStatement statement = con.prepareStatement("SELECT * FROM hwid WHERE HWID=?"))
		{
			statement.setString(1, hwid);
			
			ResultSet rset = statement.executeQuery();
			if(rset.next())
			{
				long firstTimePlayed = rset.getLong("first_time_played");
				long totalTimePlayed = rset.getLong("total_time_played")*1000;
				int pollAnswer = rset.getInt("poll_answer");
				PLAYER_THREAT threat = PLAYER_THREAT.valueOf(rset.getString("threat"));
				boolean banned = rset.getBoolean("banned");
				System.out.println("loaded answer:"+pollAnswer);
				
				foundGamer = new HwidGamer(hwid, firstTimePlayed, totalTimePlayed, pollAnswer, threat, banned);
			}
		}
		catch(Exception e)
		{
			_log.error("Failed to load Hwid("+hwid+") from Database: ", e);
		}
		System.out.println("found gamer:"+foundGamer);
		
		if(foundGamer == null)
			foundGamer = justJoinedServer(hwid);
		return foundGamer;
	}
	
	private HwidGamer justJoinedServer(String hwid)
	{
		System.out.println("just joined");
		HwidGamer newGamer = new HwidGamer(hwid, System.currentTimeMillis()/1000L, 0, -1, PLAYER_THREAT.NONE, false);
		saveNewGamer(newGamer);
		return newGamer;
	}
	
	public void updateGamerInDb(HwidGamer gamer)
	{
		try (Connection con = DatabaseFactory.getConnection();
				PreparedStatement statement = con.prepareStatement("UPDATE hwid SET first_time_played=?, total_time_played=?, poll_answer=?, threat=?, banned=? WHERE HWID=?"))
		{
			statement.setLong(1, gamer.getFirstTimePlayed());
			statement.setLong(2, gamer.getTotalTimePlayed()/1000L);
			statement.setInt(3, gamer.getPollAnswer());
			statement.setString(4, gamer.getThreat().toString());
			statement.setString(5, gamer.getHwid());
			statement.setString(6, String.valueOf(gamer.isHwidBanned()));
			statement.execute();
		}
		catch(final Exception e)
		{
			_log.error("Failed to insert Hwid("+gamer.getHwid()+") to Database: ", e);
		}
	}
	
	private void banHwidInDb(String hwid)
	{
		try (Connection con = DatabaseFactory.getConnection();
				PreparedStatement statement = con.prepareStatement("UPDATE hwid SET banned='true' WHERE HWID=?"))
		{
			statement.setString(1, hwid);
			statement.execute();
		}
		catch(final Exception e)
		{
			_log.error("Failed to ban Hwid("+hwid+") in Database: ", e);
		}
	}
	
	private void saveNewGamer(HwidGamer gamer)
	{
		System.out.println("inserting");
		try (Connection con = DatabaseFactory.getConnection();
				PreparedStatement statement = con.prepareStatement("INSERT INTO hwid SET HWID=?, first_time_played=?, total_time_played=?, poll_answer=?, threat=?, banned=?"))
		{
			statement.setString(1, gamer.getHwid());
			statement.setLong(2, gamer.getFirstTimePlayed());
			statement.setLong(3, gamer.getTotalTimePlayed()/1000L);
			statement.setInt(4, gamer.getPollAnswer());
			statement.setString(5, gamer.getThreat().toString());
			statement.setString(6, "false");//Banned
			statement.execute();
		}
		catch(final Exception e)
		{
			_log.error("Failed to insert Hwid("+gamer.getHwid()+") to Database: ", e);
		}
	}
	
	private class EveryMinuteThread extends RunnableImpl
	{
		@Override
		public void runImpl() throws Exception
		{
			String allrows = "UPDATE `hwid` ";
			for(HwidGamer gamer : _allHwids)
			{
				if(allrows.length() > 20)//About first length
					allrows += ", ";
				gamer.incTotalTimePlayed(60L);
				allrows += getSingleRow(gamer);
			}
			allrows += ";";
			
			MySql.set(allrows);
		}
		
		private String getSingleRow(HwidGamer gamer)
		{
			return "SET first_time_played="+gamer.getFirstTimePlayed()+", total_time_played="+gamer.getTotalTimePlayed()/1000L+", poll_answer="+gamer.getPollAnswer()+", threat='"+gamer.getThreat().toString()+"' WHERE HWID='"+gamer.getHwid()+"'";
		}
	}
	
	public static HwidEngine getInstance()
	{
		if(_instance == null)
			_instance = new HwidEngine();
		return _instance;
	}
}
